<?php
/**
 * リスト編集一括管理モジュール Editor
 * 利用するモジュール側で初期化して使用するイメージ？
 */
class Editor {
	public $DataID;
	public $YML;
	public $Xml;

	private $Target;
	private $Op;

	public function __construct () {
		$this->Data = array();
		$this->YML = array();
		$this->Xml = array('message'=>null);
	}
	
	public function init () {
		Y('this.Main+', YuTori::create($this, 'execMain'));
		Y('this.Main.List+', YuTori::create($this, 'execList'));
		Y('this.Main.Edit+', YuTori::create($this, 'execEdit'));

		Y('this.Data+', YuTori::create($this, 'getData'));
		Y('this.setData+', YuTori::create($this, 'setData'));
		Y('this.setMessage+', YuTori::create($this, 'setMessage'));
		Y('this.XML+', YuTori::create($this, 'getXML'));

		Y('this.YML+', YuTori::create($this, 'getYML'));

		Y('this.getOptions+', YuTori::create($this, 'getOptions'));

	}

	public function call ($Module = 'Editor', $YML = null, $Op = 'list', $Target = null, $toObj = null) {
		$this->Target = $Target;
		Y('this.Target+', $Target);
		Y('this.Module+', strtolower($Module));

		if (!($Module && $YML)) return false;
		
		$Module = explode ('/', $Module, 2);
		$Module = $Module[0];
		$YMLPath = Y('Module.Path', $Module) .'/editor/'.$YML;
		if (!file_exists ($YMLPath)) {
			Y('Logger.Debug', '指定されたYAMLファイル '.$YMLPath. ' が見つからない');
			return false;
		}
		$YMLReader = new YAMLReader ();
		$this->YML = $YMLReader->Load($YMLPath);
		
		if (!$Op) {
			if ($Default = $this->getYML('default')) {
				$Op = $Default;
			} else {
				$Op = "list";
			}
		}
		$this->Op = $Op;

		Y('this.Op+', $Op);
		Y('Logger.Debug', 'modEditor起動 [Module]'. $Module. ' [YML]'. $YML. ' [Op]'. $Op. ' [Target]'.$Target);
		
		$this->execMain();
		if (Y('Request.GET.ajax') != 'on') {
			Y('Document.load', 'Editor:editor', $toObj);
		}
		return true;
	}

	/**
	 * メイン部処理
	 */
	public function execMain () {
		Y('Logger.Debug', 'エディタ処理 '.$this->Op. ' 開始');
		
		// データ取得
		// YMLからコマンドラインを取得する
		$CMDArr = $this->getYML ($this->Op);
		if (empty($CMDArr['data'])) $CMDArr['data'] = array();

		$Rt = true;
		// キーに「cmd」が存在する場合、コマンドラインは１つとして扱う
		if (isset($CMDArr['data']['cmd'])) {
			$Method = 'cmd'.$CMDArr['data']['cmd'];
			if (method_exists($this, $Method)) {
				$Rt = $Rt && $this->$Method ($CMDArr['data']);
			} else {
				Y('Logger.Debug', 'メソッド cmd'.$Method.' がない');
				$Rt = false;
			}
		} else {
			// 各コマンドラインを実行
			foreach ((array)$CMDArr['data'] as $CMD) {
				if (!isset ($CMD['cmd'])) $Rt = false;
				$Method = 'cmd'.$CMD['cmd'];
				if (method_exists($this, $Method)) {
					$Rt = $Rt && $this->$Method ($CMD);
				} else {
					Y('Logger.Debug', 'メソッド cmd'.$Method.' がない');
					$Rt = false;
				}
			}
		}
		
//		if ($Rt) Y('Helper.list.ConnectDB.Com.endTransaction');
		
		// データをバリデートする
		if (!empty($this->Data) && is_array($this->Data) && !isset($this->Data[0])) $this->Data = array($this->Data);
		foreach ($this->Data as $Key=>$Val) {
			$validate = array();
			$this->validateArray($Val, $validate);
			$this->Data[$Key] = $validate;
		}

		if (isset($this->YML[$this->Op]['ajax']) && $this->YML[$this->Op]['ajax']) {
			Y('Document.Object+', $DOG = DocumentObjectGroup::getAllDOMs('new')->DOG_appendChild ('response'));
			Y('Document.Name', 'response.xml');
			$DOG->Type = 'xml';
			if (empty($this->Xml['message'])) {
				if ($Rt && isset($this->YML[$this->Op]['success'])) {
					$DOG->DOG_appendChild('message')->text($this->YML[$this->Op]['success']);
				} else {
					if ($Rt) {
						$DOG->DOG_appendChild('message')->text('成功しました。');
					} else {
						$DOG->DOG_appendChild('message')->text('失敗しました。');
					}
				}
			}
		} else {
			// 追加jsファイルがあれば処理する
			if (isset ($CMDArr['js'])) {
				Y('Output.addHeader', '<script src="'.$CMDArr['js'].'" type="text/javascript"></script>');
			}
			
			// テンプレートを出力
			if (!isset ($CMDArr['template'])) $CMDArr['template'] = 'Editor';
			Y('Output.Body', $CMDArr['template'].':'.$this->Op);
		}
		return $Rt;
	}

	/**
	 * eval系メソッド
	 */
	private function cmdEval ($CMD) {
		Y('Logger.Debug', 'cmdEval 開始');
		// 対象のデータを選択
		if (isset ($CMD['data'])) {
			if (!isset ($this->Data[$CMD['data']])) $this->Data[$CMD['data']] = null;
			$Target = &$this->Data[$CMD['data']];
			Y('Logger.Debug', 'Target: '.$CMD['data']);
		} else {
			$Target = &$this->Data;
			Y('Logger.Debug', 'Target: Data全体');
		}

		if (!isset ($CMD['arg'])) {
			Y('Logger.Debug', '引数なし');
			$CMD['arg'] = array ();
		}

		if (empty ($CMD['eval'])) {
			Y('Logger.Debug', 'argを代入');
			$Target = $CMD["arg"];
		} else {
			Y('Logger.Debug', 'コマンド '.$CMD['eval'].'を実行');
			$Target = call_user_func_array ($CMD['eval'], (array)$CMD["arg"]);
		}

		return true;
	}

	/**
	 * data系メソッド
	 */
	private function cmdData ($CMD) {
		Y('Logger.Debug', 'cmdData 開始');
		if (empty($CMD['data'])) $CMD['data'] = array();

		if (!strcasecmp($CMD['from'], 'validate')) {

			Y('Helper.loadAction', 'Validate', $this->getYML('validate'));
			$CMD['data'] = Y('Helper.List.Validate.exec', $this->Data);
			if ($Err = Y('Helper.List.Validate.getError')) {
				$this->Xml['message'] .= $Err;
				return false;
			}

		} elseif (!strcasecmp($CMD['from'], 'post')) {

			$CMD['data'] = Y('Request.POST');

		} elseif (!strcasecmp($CMD['from'], 'get')) {

			$CMD['data'] = Y('Request.GET');

		}
		$this->Data = array_merge($this->Data, $CMD['data']);
		return true;
	}
	
	/**
	 * database系メソッド
	 */
	private function cmdDatabase ($CMD) {
		if (!isset($CMD['query'])) return false;
		Y('Logger.Debug', 'cmdDatabase '.$CMD['query']. ' 開始');
		Y('Helper.load','ConnectDB');

//		トランザクションを使用するとlastInsertId()が使えないため、封印
//		Y('Helper.list.ConnectDB.Com.beginTransaction');

		if (!isset($CMD['data'])) $CMD['data'] = $this->Data;

		switch ($CMD['query']) {
			case ('select') :
				Y('Helper.List.ConnectDB', $CMD);
				$this->Data = Y('Helper.List.ConnectDB.Com.ResultAll');
				$this->Xml['datacount'] = count($this->Data);
				$Rt = count($this->Data);
				break;
			case ('insert') :
				$Rt = Y('Helper.loadAction', 'ConnectDB', $CMD);
				if (isset($CMD['target']) && $CMD['target']) {
					$this->Target = $Rt;
					$this->Xml['newID'] = $Rt;
				}
				break;
			case ('delete') :
				$Rt = Y('Helper.loadAction', 'ConnectDB', $CMD);
				if (empty($CMD['noxml'])) $this->Xml['datacount'] = $Rt;
				break;
			case ('update') :
				if (isset($CMD['target']) && isset($this->Data[$CMD['target']])) {
					$this->Target = $this->Data[$CMD['target']];
					$this->Xml['update'] = $this->Data[$CMD['target']];
				}
			default:
				Y('Helper.loadAction', 'ConnectDB', $CMD);
				$Rt = true;
				break;
		}
		return (bool)$Rt;
	}
	
	/**
	 * object系メソッド
	 */
	private function cmdObject ($CMD) {
		Y('Logger.Debug', 'cmdObject '.$CMD['object']. ' 開始');
		$Args[0] = $CMD['object'];
		if (isset ($CMD['arg'])) {
			Y('Logger.Debug', $CMD['arg']);
			foreach ((array)$CMD['arg'] as $Arg) {
				$Args[] = $Arg;
			}
		}
		$Rt = call_user_func_array('Y', $Args);
		if (is_bool($Rt)) {
			return $Rt;
		} elseif (is_array($Rt)) {
			Y('Logger.Debug', '戻り値が配列のため、データとして挿入');
			$this->Data = $Rt;
		}
		return true;
	}

	/**
	 * uploadメソッド
	 */
	private function cmdUpload ($CMD) {
		Y('Module.load', 'Upload');
		
		if (empty ($this->Target) || is_numeric($this->Target)) {
			Y('Module.List.Upload.uploadFile', $CMD['field'], $CMD['target']);
			return true;
		} else {
			list ($ID, $FileName) = explode ('/', urldecode($this->Target), 2);
			Y('Module.List.Upload.deleteFile', $CMD['field'], $CMD['target'], $ID, $FileName);
			$this->Xml['message'] = "削除しました。";
			$this->Xml['delete'] = "complete";
			return true;
		}
	}

	/**
	 * uploadsaveメソッド
	 */
	private function cmdUploadSave ($CMD) {
		Y('Module.load', 'Upload');
		Y('Module.List.Upload.saveFile', $this->Target);
		return true;
	}

	/**
	 * uploaddeleteメソッド
	 */
	private function cmdUploadDelete ($CMD) {
		Y('Module.load', 'Upload');
		Y('Module.List.Upload.deleteFile', '*', $CMD['target'], '*', '*');
		return true;
	}

	/**
	 * クエリから取得したデータを返す
	 */
	public function getData ($Key = null) {
		if ($Key) {
			if (isset($this->Data[$Key])) {
				return $this->Data[$Key];
			} else {
				return false;
			}
		} else {
			return $this->Data;
		}
	}
	
	/**
	 * データをセットする
	 */
	public function setData ($Key, $Value) {
		$this->Data[$Key] = $Value;
		return;
	}

	/**
	 * Optionの形式に従ってオプションリストを返す
	 */
	public function getOptions ($Option) {
		Y('Logger.Debug', 'オプションリスト取得開始 '.$Option);
		// 配列の時はそのまま返す
		if (is_array($Option)) return $Option;
		// 文字列の時はセレクターとして扱う
		if (is_string($Option)) return (array)Y($Option);
		// それ以外の時は空配列を返す
		return array();
	}

	/**
	 * エラーメッセージをセットする
	 */
	public function setMessage ($Str) {
		foreach ((array)$Str as $val) {
			$this->Xml['message'] .= $val. "\n";
		}
		return;
	}

	/**
	 * YAMLの特定の値を取り出す
	 */
	public function getYML ($Key = null) {
		if ($Key) {
			if (isset($this->YML[$Key])) {
				return $this->YML[$Key];
			} else {
				return false;
			}
		} else {
			return $this->YML;
		}
	}

	/**
	 * XMLの特定の値を取り出す
	 */
	public function getXML ($Key = null) {
		if ($Key) {
			if (isset($this->Xml[$Key])) {
				return $this->Xml[$Key];
			} else {
				return false;
			}
		} else {
			return $this->Xml;
		}
	}
	
	/**
	 * データバリデート用 配列を再帰的に一次元化する
	 */
	private function validateArray ($Arr, &$NewArr, $BaseKey = null) {
		foreach ((array)$Arr as $Key=>$Val) {
			if ($BaseKey) $Key = $BaseKey . '['.$Key.']';
			if (is_array($Val)) {
				$this->validateArray ($Val, $NewArr, $Key);
			} else {
				$NewArr[$Key] = $Val;
			}
		}
	}
}
?>